/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.kairos.utils.monitor
{
	import com.kairos.utils.MonitorColorUtils;	
	import com.bourre.log.PixlibStringifier;
	import com.bourre.structures.Range;
	import com.kairos.engine.RTEvent;

	/**
	 * Abstract class for all objects used as field by the <code>Monitor</code>.
	 * 
	 * <p>Extends that class and override the <code>registerValue</code> method
	 * to create your own behavior. See <code>TimeStatistic</code> or <code>FPSStatistic</code>
	 * for an example of concret statistic field.</p>
	 * 
	 * @author 	Cédric Néhémie
	 * @see		MonitorField
	 * @see		Monitor
	 * @example	Create your own monitor field by extending <code>AbstractMonitorField</code>
	 * <listing>package myPackage
	 * {
	 * 	import com.bourre.structures.Range;
	 * 	import com.kairos.engine.RTEvent;
	 * 	import com.kairos.utils.monitor.AbstractMonitorField;
	 * 	import com.kairos.utils.monitor.MonitorField;
	 * 
	 * 	public class MyField extends AbstractMonitorField implements MonitorField
	 * 	{
	 * 		public function MyField()
	 * 		{
	 * 			super( "My Field Label" , 0, "ms", new Range ( 0, 50 ) );
	 * 		}
	 * 		// Simply override the registerValue method to activate the field.
	 * 		public override function registerValue ( e : RTEvent ) : void
	 * 		{
	 * 			_nLastValue = _nValue;
	 * 			_nValue = AnyObject.count;
	 * 		}
	 * 	}
	 * }</listing>
	 * 
	 * If you don't want to display your field as a curve of the graph, simply override
	 * the <code>isCurve</code> method and return <code>false</code>. 
	 * 
	 * <listing>package myPackage
	 * {
	 * 	import com.bourre.structures.Range;
	 * 	import com.kairos.engine.RTEvent;
	 * 	import com.kairos.utils.monitor.AbstractMonitorField;
	 * 	import com.kairos.utils.monitor.MonitorField;
	 * 
	 * 	public class MyField extends AbstractMonitorField implements MonitorField
	 * 	{
	 * 		public function MyField()
	 * 		{
	 * 			super( "My Field Label" , 0, "ms", new Range ( 0, 50 ) );
	 * 		}
	 * 		// Simply override the registerValue method to activate the field.
	 * 		public override function registerValue ( e : RTEvent ) : void
	 * 		{
	 * 			_nLastValue = _nValue;
	 * 			_nValue = AnyObject.count;
	 * 		}
	 * 		// The field is not drawn as a curve in the graph.
	 * 		public override function isCurve () : Boolean
	 * 		{
	 * 			return false;
	 * 		}
	 * 	}
	 * }</listing>
	 */
	public class AbstractMonitorField implements MonitorField
	{
		/**
		 * Name of the field in the monitor.
		 */
		protected var _sName : String;
		
		/**
		 * Unit for the field value such ms, fps, or any other unit.
		 */
		protected var _sUnit : String;
		
		/**
		 * Name of the field in the monitor.
		 */
		protected var _nColor : Number;
		/**
		 * Current value for the field 
		 */
		protected var _nValue : Number;
				
		/**
		 * Last value of the field, used to draw a line
		 */
		protected var _nLastValue : Number;
		
		/**
		 * Height of the bitmap of the monitor's graph
		 */
		protected var _nHeight : Number;
		
		/**
		 * Range of value to display in the graph.
		 */
		protected var _rDisplayRange : Range;
		
		/**
		* Display state of the curve
		*/
		protected var _bEnabled : Boolean;
		
		/**
		 * Call the super constructor to defines common properties of the field.
		 * 
		 * @param name	<code>String</code> name of the field.
		 * @param val	<code>Number</code> start value of the field.
		 * @param unit	<code>String</code> of the unit for the field.
		 * @param dr	<code>Range</code> of displaed values.
		 * @param col	<code>Number</code> color of the field.
		 */
		public function AbstractMonitorField ( 	name : String = "Untitled",
												val : Number = 0, 
												unit : String = "ms", 												 
												dr : Range = null,
												col:Number = NaN )
		{
			_sName = name;
			_nLastValue = _nValue = val;
			_sUnit = unit;
			_nColor = col;
			_rDisplayRange = dr != null ? dr : new Range( 0, 100 );
			_bEnabled = true;
		}
		
		/**
		 * @inheritDoc
		 */
		public function getFormattedValue():String
		{
			return _nValue + " " + _sUnit;
		}
		
		/**
		 * @inheritDoc
		 */
		public function getName () : String
		{
			return _sName;
		}
		
		/**
		 * Returns the range between the last value and the current value
		 * of the field in the display bitmap coordinates.
		 * 
		 * <p>The range contained values in pixels according to the
		 * graph's bitmap height passed to this object when added
		 * to the <code>Monitor</code>.</p>
		 * 
		 * @return <code>Range</code> of value variations.
		 */
		public function getDifference():Range
		{
			return new Range ( _getDisplayValue( _nLastValue ), _getDisplayValue( _nValue ) );
		}
		
		/**
		 * @inheritDoc
		 */
		public function getColor():Number
		{
			if( isNaN( _nColor ) ) setColor( MonitorColorUtils.get() );
			return _nColor;
		}
		
		/**
		 * @inheritDoc
		 */
		public function setDisplayRange( r : Range ) : void
		{
			_rDisplayRange = r;
		}
		
		/**
		 * @inheritDoc
		 */
		public function setDisplayHeight( n:Number ) : void
		{
			_nHeight = n;
		}
		
		/**
		 * @inheritDoc
		 */
		public function setColor( col : Number ) : void
		{
			_nColor = col;
		}
		
		/**
		 * @inheritDoc
		 */
		public function isCurve() : Boolean
		{
			return true;
		}
		
		/**
		 * @inheritDoc
		 */
		public function isLabel() : Boolean
		{
			return true;
		}
		
		/**
		 * <p>Override that method to register your own value.</p>
		 * 
		 * @inheritDoc
		 */
		public function registerValue ( e : RTEvent ) : void {}
		
		
		/**
		 * Returns the value in graph coordinates according to the display range
		 * and the display height.
		 * 
		 * @param	v	<code>Number</code> value of the field
		 * @return 	<code>Number</code> value in display bitmap coordinates.
		 */
		protected function _getDisplayValue ( v : Number ) : Number
		{
			return _nHeight - Math.floor( ( ( v - _rDisplayRange.min ) / _rDisplayRange.max ) * _nHeight );
		}
		
		/**
		 * Returns the <code>String</code> representation of the object.
		 * 
		 * @return <code>String</code> representation of the object.
		 */
		public function toString() : String
		{
			return PixlibStringifier.stringify( this );
		}
		
		/**
		 * @inheritDoc
		 */
		public function setEnabled ( b : Boolean ) : void
		{
			_bEnabled = b;
		}
		
		/**
		 * @inheritDoc
		 */
		public function isEnabled () : Boolean
		{
			return _bEnabled;
		}
	}
}